/**
 * @Title: DynamicMapperScanner.java
 * @Package com.ssd.demo.base.core.mybatismapper
 * @Description: TODOo
 * @author xiaoruihu 2016年5月4日 上午11:16:24
 * @version V1.0
 */
package com.ssd.demo.base.core.mybatismapper;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.UUID;

import javax.persistence.Table;

import org.apache.ibatis.javassist.ClassClassPath;
import org.apache.ibatis.javassist.ClassPool;
import org.apache.ibatis.javassist.CtClass;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;

import tk.mybatis.spring.mapper.MapperFactoryBean;

import com.ssd.demo.base.core.BaseMapper;
import com.ssd.demo.base.core.BaseService;
import com.ssd.demo.base.util.DynamicUtils;

/**
 * @Description: DynamicMapperScanner
 * @author xiaoruihu 2016年5月4日 上午11:16:24
 */
public class MapperScannerConfigurer extends tk.mybatis.spring.mapper.MapperScannerConfigurer {

    public static void main(String[] args) {
        MapperScannerConfigurer dd = new MapperScannerConfigurer();

        dd.dynmicBaseService("com.ssd.demo", BaseService.class, null);
    }

    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
        super.postProcessBeanDefinitionRegistry(registry);

        // 生成动态mapper
        String basePackage = "com.ssd.demo";
        // 1 扫描存在entity但是不存在mapper的entity
        // 2 生成mapper的接口
        // 3 按照mybatis和tkmapper的规则注入到spring中
        dynmicBaseMapper(basePackage, BaseMapper.class, registry);

        // 生成动态service
        dynmicBaseService(basePackage, BaseService.class, registry);

    }

    public void dynmicBaseService(String basePackage, Class<?> baserService, BeanDefinitionRegistry registry) {
        Set<Class<?>> tableSetEntity = getNotBaseClassEntity(basePackage, baserService, false);
        List<Class<?>> list = new ArrayList<Class<?>>();
        for (Class<?> c : tableSetEntity) {
            String classname = "Randome" + c.getSimpleName() + "BaseService";
            list.add(generateChildreBaseService(classname, basePackage, baserService, c));
        }

        for (Class<?> clazz : list) {
            // 通过BeanDefinitionBuilder创建bean定义
            BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(clazz);
            // 注册bean
            String beanname = UUID.randomUUID().toString() + "_" + clazz.getSimpleName();
            BeanDefinition definition = beanDefinitionBuilder.getRawBeanDefinition();
            definition.setBeanClassName(clazz.getName());
            registry.registerBeanDefinition(beanname, definition);
        }

    }

    /**
     * @Title: generateMapperInterface
     * @Description: 生成mapper接口
     * @author xiaoruihu
     * @param className
     * @param packagename
     * @param baseClass
     * @param TClass
     * @return
     */
    public Class<?> generateChildreBaseService(String className, String packagename, Class<?> baseClass, Class<?> TClass) {
        try {
            String genString = "<L" + TClass.getName().replace(".", "/") + ";>";
            String beseString = "L" + baseClass.getName().replace(".", "/");
            ClassPool pool = ClassPool.getDefault();
            pool.insertClassPath(new ClassClassPath(this.getClass()));
            CtClass superIn = pool.get(baseClass.getName());
            CtClass ct = pool.makeClass(packagename + "." + className);
            ct.setGenericSignature(beseString + genString + ";");
            ct.setSuperclass(superIn);
            return ct.toClass();
        }
        catch (Exception e) {
            throw new RuntimeException("动态生成类失败", e);
        }
    }

    /**
     * @Title: dynmicBaseMapper
     * @Description: 动态生成mapper 注册到spring中
     * @author xiaoruihu
     * @param packageName
     * @param baseMapper
     * @param registry
     */
    public void dynmicBaseMapper(String packageName, Class<?> baseMapper, BeanDefinitionRegistry registry) {
        List<Class<?>> list = generateMapper("com.ssd.demo", baseMapper);
        for (Class<?> clazz : list) {

            // 通过BeanDefinitionBuilder创建bean定义
            BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(clazz);
            // 注册bean
            String beanname = UUID.randomUUID().toString() + "_" + clazz.getSimpleName();
            BeanDefinition definition = beanDefinitionBuilder.getRawBeanDefinition();
            definition.setBeanClassName(MapperFactoryBean.class.getName());
            definition.getPropertyValues().add("mapperHelper", this.getMapperHelper());
            definition.getPropertyValues().add("addToConfig", true);
            definition.getPropertyValues().add("mapperInterface", clazz);
            definition.getPropertyValues().add("sqlSessionFactory", new RuntimeBeanReference("sqlSessionFactory"));
            registry.registerBeanDefinition(beanname, definition);
        }
    }

    /**
     * @Title: generateMapper
     * @Description: 动态生成不存在的mapperClass
     * @author xiaoruihu
     * @param basePackage
     * @param baseMapper
     * @return
     */
    public  List<Class<?>> generateMapper(String basePackage, Class<?> baseMapper) {
        Set<Class<?>> tableSetEntity = getNotBaseClassEntity(basePackage, baseMapper, true);

        List<Class<?>> list = new ArrayList<Class<?>>();
        for (Class<?> c : tableSetEntity) {
            String classname = "Randome" + c.getSimpleName() + "Mapper";
            list.add(generateMapperInterface(classname, basePackage, baseMapper, c));
        }

        return list;
    }

    public static Set<Class<?>> getNotBaseClassEntity(String basePackage, Class<?> baseMapper, boolean isInterface) {
        Set<Class<?>> tableSetEntity = DynamicUtils.getClassSetAnnotation(basePackage, Table.class);
        Set<Class<?>> MapperSet = DynamicUtils.getClassSetSuperClass(basePackage, baseMapper);
        for (Class<?> mapper : MapperSet) {
            Class<?> clazz = null;
            if (isInterface) {
                clazz = DynamicUtils.getInterfafceActualTypeClass(mapper);
            } else {
                clazz = DynamicUtils.getClassActualTypeClass(mapper);
            }
            for (Class<?> entity : tableSetEntity) {
                if (entity == clazz) {
                    tableSetEntity.remove(entity);
                    break;
                }
            }
        }
        return tableSetEntity;
    }

    /**
     * @Title: generateMapperInterface
     * @Description: 生成mapper接口
     * @author xiaoruihu
     * @param className
     * @param packagename
     * @param baseClass
     * @param TClass
     * @return
     */
    public Class<?> generateMapperInterface(String className, String packagename, Class<?> baseClass, Class<?> TClass) {
        try {
            String genString = "<L" + TClass.getName().replace(".", "/") + ";>";
            String beseString = "L" + baseClass.getName().replace(".", "/");
            ClassPool pool = ClassPool.getDefault();
            pool.insertClassPath(new ClassClassPath(this.getClass()));
            CtClass superIn = pool.get(baseClass.getName());
            CtClass ct = pool.makeInterface(packagename + "." + className);
            ct.setGenericSignature("Ljava/lang/Object;" + beseString + genString + ";");
            ct.setInterfaces(new CtClass[] { superIn });
            return ct.toClass();
        }
        catch (Exception e) {
            throw new RuntimeException("动态生成接口失败", e);
        }
    }
}
